#include "Socket.h"
using namespace std;

#ifdef __LINUXGCC__
Socket::Socket(uint32_t ip, uint16_t prt) {
	char ip_name[50]={0};
	get_ip_from_int(ip,ip_name);
	init(ip_name,prt);
}

Socket::Socket(char* host, uint16_t prt) {
	init(host,prt);
}

void Socket::init(char* host, uint16_t prt) {
	port=prt;
	struct hostent* server=gethostbyname(host);
	if(server==NULL) {
		fd=-1;
		return;
	}
	struct sockaddr_in address;
	address.sin_family = AF_INET;
	bcopy((char*)server->h_addr, (char*)&(address.sin_addr.s_addr), server->h_length);
	address.sin_port = htons(prt);
	fd=socket(AF_INET,SOCK_STREAM,0);
	//TODO: shorten the connection timeout significantly.  If the attempted port isn't opened, it takes forever to realize it.
	if(fd<0 || connect(fd,(struct sockaddr*)&address,sizeof(address)) < 0) {
		close(fd);
		fd=-1;
		return;
	}
}

Socket::Socket(uint16_t prt) {
	port=prt;
	fd=socket(AF_INET,SOCK_STREAM,0);
	if(fd<0) {
		close(fd);
		fd=-1;
		return;
	}
	// prepare socket address //
	struct sockaddr_in address;
	address.sin_family = AF_INET;
	address.sin_addr.s_addr = INADDR_ANY;
	address.sin_port = htons(prt);
	
	// avoid EADDRINUSE error on bind //
	int optval=1;
	if( setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0) {
		close(fd);
		fd=-1;
		return;
	}
	// bind, listen, accept //
	if( bind(fd,(struct sockaddr*)&address, sizeof(address)) < 0) {
		close(fd);
		fd=-1;
		return;
	}
}

Socket Socket::accept_connection() {
	struct sockaddr_in address;
	address.sin_family = AF_INET;
	address.sin_addr.s_addr = INADDR_ANY;
	address.sin_port = htons(port);
	if(listen(fd,20)<0) {
		close(fd);
		fd=-1;
		return Socket(-1);
	}
	unsigned int addrlen = sizeof(struct sockaddr_in);
	int connection_fd = accept(fd,(struct sockaddr*)&address, &addrlen);
	if(connection_fd<0) {
		close(fd);
		fd=-1;
		return Socket(-1);
	}
	return Socket(connection_fd);
}

int Socket::read(char* dest, int max_len) {
	int r=recv(fd,dest,max_len,0);
	if(r<0) {
		close(fd);
		fd=-1;
	}
	return r;
}

int Socket::write(char* message, int length) {
	int w=send(fd,message,length,0);
	if(w<0) {
		close(fd);
		fd=-1;
	}
	return w;
}

Socket::Socket(int cfd) {
	fd=cfd;
}

bool Socket::is_alive() {
	return fd>=0;
}

Socket::~Socket() {
	if(fd>=0)
		close(fd);
}

#endif
//TODO: Implement on Mac and Windows.
